package org.mtmi.ui.edit;

import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.mtmi.tuio.listeners.DragListener;
import org.mtmi.tuio.listeners.SingleTapListener;
import org.mtmi.tuio.listeners.TUIODragEvent;
import org.mtmi.tuio.listeners.TUIOTapEvent;
import org.mtmi.ui.GraphicUtil;
import org.mtmi.ui.IAdaptable;
import org.mtmi.ui.IMG;
import org.mtmi.ui.M;
import org.mtmi.ui.controls.Control;
import org.mtmi.ui.controls.ITouchableControl;
import org.mtmi.ui.controls.IVisualControl;
import org.mtmi.ui.controls.TouchControlEvent;
import org.mtmi.ui.drawables.IDrawable;
import org.mtmi.ui.layouts.LayoutData;
import org.mtmi.ui.links.CID;
import org.mtmi.ui.links.IConnectionOutlinePointProvider;
import org.mtmi.ui.links.IConnectable;
import org.mtmi.ui.scenes.COLOR;
import org.mtmi.ui.scenes.Scene;

/**
 * Scene editor Control able to "connect" to a connection provider control on scene (the connected control).
 * See usage to learn what it is really ...
 * <p>
 * This control is the target with a known connection id.
 * User may drag from it a link to a source (connection provider).
 */
public abstract class AbstractSourceControlConnector extends Control 
  implements ITouchableControl, SingleTapListener, DragListener
{
	private static final int MG=5; //margin around image;
	
	private IDrawable image; //image displayed in connector
	private String connection_id; //the id of connection this connector manages (the source cid)

	private IConnectable connected_control; //the control that accept connect from this
	private String       connected_control_cid; //connection id from connected control
	
	/** location must be set by user */
	public AbstractSourceControlConnector( Scene scene, IDrawable img, String _connection_id, int _flags ) {
		super( scene, _flags | M.SCENE_EDITOR_CONTROL );
		
		connection_id = _connection_id;
		image = img;
		setSize( computeSize( new LayoutData( scene.getGC(),0,0,0,0) ) );
		
		setForeground( COLOR.yellow );
		setBackground( COLOR.dark_yellow );
	}

	public void setConnectedControl( IConnectable t, String cid ) {
		connected_control=t;
		connected_control_cid = cid;
System.out.println(">> SourceConnection, initiate with cid="+cid+" connectable="+t);		
	}
	
	public IConnectable getConnectedControl() { return connected_control; }
	public String getConnectedControlConnectionId() { return connected_control_cid; }
	
	@Override
	public Point computeSize(LayoutData l) {
/*TODO:OLD:		Point sz = image.computeSize( l.lw, l.lh );
		sz.x += 2*MG;
		sz.y += 2*MG;
		return sz;*/
		Image img = IMG.Get( IMG.I_CONNECTOR_UP_BG );
		Rectangle r = img.getBounds();
		return new Point( r.width, r.height );
	}

	private static final int LW = 4; //line width
	private static final int LW2 = 2*LW; //line width
	private static final int ARROW_DIM = 15;
	
	public static void paintFlyingLink( GC gc, int sx, int sy, int tx, int ty, boolean forbidden_link )
	{
		if( forbidden_link ) {
			gc.setForeground( COLOR.red );
			gc.setBackground( COLOR.light_red );
		} else {
			gc.setForeground( COLOR.green );
			gc.setBackground( COLOR.light_green );
		}
		gc.setLineWidth( LW );
		gc.drawLine( sx, sy, tx, ty );
		GraphicUtil.drawArrow( gc, tx, ty, sx, sy, 0.5f, ARROW_DIM );
	}
	
	public static Point getAnchorPoint( int sx, int sy, IVisualControl target, String cid ) {
			IConnectionOutlinePointProvider cp = null;
			if( target instanceof IAdaptable ) {
				cp = (IConnectionOutlinePointProvider)((IAdaptable)target).getAdapter( IConnectionOutlinePointProvider.class, cid );
			}
			//by default use a chopbox:
			Point p;
			if( cp==null ) {
				Point l = target.getLocation();
				Point s = target.getSize();
				p = GraphicUtil.pointToCenterOutlinePoint( l, s, sx, sy );
			} else {
				p = cp.getOutlinePoint( sx, sy );
			}
			return p;
	}
	
	public static void paintLink( GC gc, int sx, int sy, IVisualControl target, String cid )
	{
		if( target!=null ) {
System.out.println("SourceConnector to control="+target);
			gc.setForeground( COLOR.green );
			gc.setBackground( COLOR.light_green );
			gc.setLineWidth( LW );
			Point p = getAnchorPoint( sx, sy, target, cid );
			gc.drawLine( sx, sy, p.x, p.y );
			GraphicUtil.drawArrow( gc, p.x, p.y, sx, sy, 0.5f, ARROW_DIM );
		}
	}
	
	@Override
	public void paint(GC gc)
	{
		int cx = location.x+size.x/2;
		int cy = location.y+size.y/2;
		
		//wire from connector to current dragging location
		if( state==2 ) {
			paintFlyingLink( gc, cx, cy, dx ,dy, current_accepted_control==null );
		} else {
			paintLink( gc, cx, cy, (IVisualControl)connected_control, connected_control_cid );
		}
		
		//connector shape (over connection line)
/*TODO:OLD:		int xy[] = new int[] {
				location.x, location.y+size.x/4,
				location.x+size.x/2, location.y,
				location.x+size.x, location.y+size.x/4,
				location.x+size.x, location.y+size.y,
				location.x, location.y+size.y,
		};
		gc.setBackground( getBackground() );
		//gc.fillRectangle( location.x, location.y, size.x, size.y );
		gc.fillPolygon( xy );
		gc.setForeground( getForeground() );
		gc.setLineWidth( 3 );
		//gc.drawRectangle( location.x, location.y, size.x, size.y );
		gc.drawPolygon( xy ); 
		
		//image is centered:
		Point sz = image.computeSize( size.x-2*MG, size.y-2*MG );
		int x = location.x + (size.x-sz.x)/2;
		int y = location.y + (size.y-sz.y)/2;
		int st = state==0 ? IDrawable.NORMAL : IDrawable.PRESSED;
		image.paint( gc, x, y, sz.x, sz.y, st ); */
		
		//connector image (over link)
		Image img = IMG.Get( IMG.I_CONNECTOR_UP_BG );
		gc.drawImage( img, location.x, location.y );
		
		Point sz = image.computeSize( size.x-2*MG, size.y-2*MG-16 );
		int x = location.x+(size.x-sz.x)/2;
		int y = location.y+(size.y-16-sz.y)/2+16; //16: the arrow of background connector image
		int st = state==0 ? IDrawable.NORMAL : IDrawable.PRESSED;
		image.paint( gc, x, y, sz.x, sz.y, st );
		gc.setLineWidth( 0 );
	}

	@Override
	public boolean containsTouch(TouchControlEvent e) {
		return GraphicUtil.rectContains( location.x, location.y, size.x, size.y, e.x, e.y );
	}

	private int state; //0: none, 1:tapped, 2:dragging, 3:tapUp (for redraw area)
	private int dx, dy; //if state !=0 the last move to draw line
	private IConnectable current_accepted_control;
	private String       current_accepted_cid;
	
	@Override
	public void tapDown( TUIOTapEvent event ) {
		state=1;
		current_accepted_control=null;
		current_accepted_cid = null;
	}

	@Override
	public void tapUp(TUIOTapEvent event) {
		connected_control = current_accepted_control;
		connected_control_cid = current_accepted_cid;
System.out.println(">> SourceConnector connecting cid="+connected_control_cid+" control="+connected_control);
		newConnection( current_accepted_control, connected_control_cid, dx, dy, connection_id );
		redraw(); //redraw before change state: the area must include connection
		state=0;
	}

	/** 
	 * @return true if control is accepted as a target of connector */
//	protected abstract boolean isConnectionAccepted( ITouchableControl c, int x, int y );
	
	/** called when a new connection is established */
	protected abstract void newConnection( IConnectable c, String connectable_cid, int x, int y, String source_cid );
	
	@Override
	public void drag( TUIODragEvent event ) {
		if( state!=0 ) {
			state=2;
			
			//the area to redraw: (including line width)
			Rectangle r = getBounds( null );
			GraphicUtil.expandRect( r, dx-LW, dy-LW, LW2, LW2  );
			
			dx = event.ex;
			dy = event.ey;
			
			GraphicUtil.expandRect( r, dx-LW, dy-LW, LW2, LW2  );
			//redraw area must also include the arrow of link:
			GraphicUtil.expandRect( r, ARROW_DIM/2+LW );
			
			
			ITouchableControl c = getScene().getControlAt( event.ex, event.ey );
			current_accepted_control = null;
			if( c instanceof IConnectable ) {
				IConnectable cp = (IConnectable)c;
				String cid = cp.getConnectionAt( event.ex ,event.ey );
				if( cid!=null && CID.IsValidConnection( cid, connection_id ) ) {
					current_accepted_control = cp;
					current_accepted_cid = cid;
				}
			}
			
			getScene().redraw( r.x, r.y, r.width, r.height, true );
			//redraw();
		}
	}
	
	@Override
	public Rectangle getRedrawArea( Rectangle ar ) {
		//the area to redraw: (including line width)
		Rectangle r = getBounds( ar );
		
		if( state!=1 ) {
			if( connected_control!= null ) {
				//add are for link:
				Point p = getAnchorPoint( location.x+size.x/2, location.y+size.y/2, (IVisualControl)connected_control, connected_control_cid );
				//including line width:
				GraphicUtil.expandRect( r, p.x-LW, p.y-LW, LW2, LW2 );
				//including arrow size:
				GraphicUtil.expandRect( r, ARROW_DIM/2+LW );
			}
		}
		if( state == 2 ) { //tapUp: refresh also the last link location
			//defining/defined link to point dx,dy
			
			//including line width:
			GraphicUtil.expandRect( r, dx-LW, dy-LW, LW2, LW2 );
			//including arrow size:
			GraphicUtil.expandRect( r, ARROW_DIM/2+LW );
		}
		
		return r;
	}
	

}
